#ifndef _DXYREFLECTCLASS_H_
#define _DXYREFLECTCLASS_H_

#include <map>
#include <string>
#include <functional>
#include <memory>
#include <exception>
#include <cstring>
#include <stdexcept>

namespace dxy {
namespace reflect {

    #ifdef _WIN32
        #define DXY_EXPORT_API __declspec(dllexport)
        #define DXY_INPORT_API __declspec(dllinport)
    #elif __linux__
        #define DXY_EXPORT_API 
        #define DXY_INPORT_API
    #endif

    
    class DXY_EXPORT_API DxyRelfectObject;

    class DXY_EXPORT_API DxyReflectClass
    {
        friend DxyRelfectObject;
    public:
        static DxyReflectClass & getInstance();

        template<class T>
        void registerClassByName(const std::string & className, std::function<T * (void)> && f);
        std::unique_ptr<DxyRelfectObject> getClassByName(const std::string & className);
        void registerClassFieldByName(const std::string & className, const std::string & fieldName, size_t offset);
        void registerClassFunctionByName(const std::string & className, const std::string & funcName, size_t ptr);

    private:
        DxyReflectClass();
        ~DxyReflectClass();


    private:
        std::map<std::string, std::function<DxyRelfectObject * (void)>>         m_classMap;
        std::map<std::string, std::map<std::string, size_t>>                    m_classFieldMap;
        std::map<std::string, std::map<std::string, size_t>>                    m_classFunctionMap;
    };

    template<class T>
    void DxyReflectClass::registerClassByName(const std::string & className, std::function<T * (void)> && f)
    {
        m_classMap[className] = std::forward<std::function<T * (void)>>(f);
    }


    class DXY_EXPORT_API DxyRelfectObject
    {
    public:
        virtual ~DxyRelfectObject();

        void call(const std::string & className, const std::string & funcName);

        template<class Ret>
        Ret call(const std::string & className, const std::string & funcName);

        template<class... Args>
        void call(const std::string & className, const std::string & funcName, Args&&... args);
        
        template<class Ret, class... Args>
        Ret call(const std::string & className, const std::string & funcName, Args&&... args);

        template<class T>
        void set(const std::string & className, const std::string & fieldName, const T & src);
        template<class T>
        void get(const std::string & className, const std::string & fieldName, T & src);
    };

    template<class T>
    void DxyRelfectObject::set(const std::string & className, const std::string & fieldName, const T & src)
    {
        auto it1 = DxyReflectClass::getInstance().m_classFieldMap.find(className);
        if(it1 == DxyReflectClass::getInstance().m_classFieldMap.end()) throw std::runtime_error("Can not find class");
        auto it2 = DxyReflectClass::getInstance().m_classFieldMap[className].find(fieldName);
        if(it2 == DxyReflectClass::getInstance().m_classFieldMap[className].end()) throw std::runtime_error("Can not find field");
        size_t p = (size_t)this + it2->second;
        *((T*)p) = src;
    }

    template<class T>
    void DxyRelfectObject::get(const std::string & className, const std::string & fieldName, T & src)
    {
        auto it1 = DxyReflectClass::getInstance().m_classFieldMap.find(className);
        if(it1 == DxyReflectClass::getInstance().m_classFieldMap.end()) throw std::runtime_error("Can not find class");
        auto it2 = DxyReflectClass::getInstance().m_classFieldMap[className].find(fieldName);
        if(it2 == DxyReflectClass::getInstance().m_classFieldMap[className].end()) throw std::runtime_error("Can not find field"); 
        size_t p = (size_t)this + it2->second;
        src = *((T*)p);
    }

    template<class Ret>
    Ret DxyRelfectObject::call(const std::string & className, const std::string & funcName)
    {
        auto it1 = DxyReflectClass::getInstance().m_classFunctionMap.find(className);
        if(it1 == DxyReflectClass::getInstance().m_classFunctionMap.end()) throw std::runtime_error("Can not find class");
        auto it2 = DxyReflectClass::getInstance().m_classFunctionMap[className].find(funcName);
        if(it2 == DxyReflectClass::getInstance().m_classFunctionMap[className].end()) throw std::runtime_error("Can not find function");
        auto func = *(std::function<Ret (decltype(this))>*)(it2->second);
        return func(this);
    }

    template<class... Args>
    void DxyRelfectObject::call(const std::string & className, const std::string & funcName, Args&&... args)
    {
        auto it1 = DxyReflectClass::getInstance().m_classFunctionMap.find(className);
        if(it1 == DxyReflectClass::getInstance().m_classFunctionMap.end()) throw std::runtime_error("Can not find class");
        auto it2 = DxyReflectClass::getInstance().m_classFunctionMap[className].find(funcName);
        if(it2 == DxyReflectClass::getInstance().m_classFunctionMap[className].end()) throw std::runtime_error("Can not find function");
        auto func = *(std::function<void (decltype(this), Args&&...)>*)(it2->second);
        func(this, std::forward<Args>(args)...);
    }

    template<class Ret, class... Args>
    Ret DxyRelfectObject::call(const std::string & className, const std::string & funcName, Args&&... args)
    {
        auto it1 = DxyReflectClass::getInstance().m_classFunctionMap.find(className);
        if(it1 == DxyReflectClass::getInstance().m_classFunctionMap.end()) throw std::runtime_error("Can not find class");
        auto it2 = DxyReflectClass::getInstance().m_classFunctionMap[className].find(funcName);
        if(it2 == DxyReflectClass::getInstance().m_classFunctionMap[className].end()) throw std::runtime_error("Can not find function");
        auto func = *(std::function<Ret (decltype(this), Args&&...)>*)(it2->second);
        return func(this, std::forward<Args>(args)...);
    }

    template<class T>
    class DxyClassRegister
    {
    public:
        DxyClassRegister(const std::string & className, std::function<T * (void)> && f)
        {
            DxyReflectClass::getInstance().registerClassByName(className, std::forward<std::function<T * (void)>>(f));   
        }
    };

    class DXY_EXPORT_API DxyFieldRegister
    {
    public:
        DxyFieldRegister(const std::string & className, const std::string & fieldName, size_t offset)
        {
            DxyReflectClass::getInstance().registerClassFieldByName(className, fieldName, offset);   
        }
    };

    class DXY_EXPORT_API DxyFunctionRegister
    {
    public:
        DxyFunctionRegister(const std::string & className, const std::string & funcName, size_t ptr)
        {
            DxyReflectClass::getInstance().registerClassFunctionByName(className, funcName, ptr);
        }
    };

    #define CLASS_REGISTER(className) \
        dxy::reflect::DxyClassRegister<className> dxyClassRegister##className(#className, [] () {return new className();}); \

    #define CLASS_FIELD_REGISTER(className, fieldName) \
        className tmp##className##fieldName; \
        dxy::reflect::DxyFieldRegister dxyFieldRegister##className##fieldName(#className, #fieldName, ((size_t)&tmp##className##fieldName.fieldName - (size_t)&tmp##className##fieldName)); \

    #define CLASS_FUNC_REGISTER(className, funcName) \
        std::function<void (className*)> tmpFunc##className##funcName = &className::funcName; \
        dxy::reflect::DxyFunctionRegister dxyFunctionRegister##className##funcName(#className, #funcName, (size_t)&tmpFunc##className##funcName);

    #define CLASS_FUNC_REGISTER_RET(className, funcName, ret) \
        std::function<ret (className*)> tmpFunc##className##funcName = &className::funcName; \
        dxy::reflect::DxyFunctionRegister dxyFunctionRegister##className##funcName(#className, #funcName, (size_t)&tmpFunc##className##funcName);

    #define CLASS_FUNC_REGISTER_ARGS(className, funcName, ...) \
        std::function<void (className*, __VA_ARGS__)> tmpFunc##className##funcName = &className::funcName; \
        dxy::reflect::DxyFunctionRegister dxyFunctionRegister##className##funcName(#className, #funcName, (size_t)&tmpFunc##className##funcName);

    #define CLASS_FUNC_REGISTER_ARGS_RET(className, funcName, ret, ...) \
        std::function<ret (className*, __VA_ARGS__)> tmpFunc##className##funcName = &className::funcName; \
        dxy::reflect::DxyFunctionRegister dxyFunctionRegister##className##funcName(#className, #funcName, (size_t)&tmpFunc##className##funcName);

}
}

#endif